/*
 * Copyright © 2018 CODESTD.COM Inc. All rights reserved.
 */
package com.codestd.security.config;

import com.codestd.security.mapper.SysResourceMapper;
import com.codestd.security.mapper.SysRoleMapper;
import com.codestd.security.shiro.filter.CaptchaFormAuthenticationFilter;
import com.codestd.security.shiro.filter.DynamicShiroFilterFactoryBean;
import com.codestd.security.shiro.filter.RolePermissionAuthorizationFilter;
import com.codestd.security.shiro.listener.ShiroSessionListener;
import com.codestd.security.shiro.matcher.Md5CredentialsMatcher;
import com.codestd.security.shiro.realm.DatabaseRealm;
import com.codestd.security.shiro.realm.MemoryRolePermissionResolver;
import org.apache.shiro.authz.permission.RolePermissionResolver;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Shiro 配置
 *
 * @author jaune
 * @since 1.0.0
 */
@Configuration
public class ShiroConfiguration {

    @Bean
    public SessionListener sessionListener() {
        return new ShiroSessionListener();
    }

    @Bean
    public EhCacheManager ehCacheManager() {
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

    @Bean
    public DefaultWebSessionManager sessionManager(
            @Qualifier("sessionListener") SessionListener sessionListener) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.getSessionListeners().add(sessionListener);
        return sessionManager;
    }

    @Bean
    public MemoryRolePermissionResolver rolePermissionResolver(
            @Qualifier("sysRoleMapper")SysRoleMapper sysRoleMapper) {
        MemoryRolePermissionResolver rolePermissionResolver = new MemoryRolePermissionResolver();
        rolePermissionResolver.setSysRoleMapper(sysRoleMapper);
        return rolePermissionResolver;
    }

    /**
     * 凭证管理
     */
    @Bean
    public DatabaseRealm realm(@Qualifier("ehCacheManager") CacheManager cacheManager,
                               @Qualifier("rolePermissionResolver") RolePermissionResolver rolePermissionResolver) {
        DatabaseRealm realm = new DatabaseRealm();
        realm.setCredentialsMatcher(new Md5CredentialsMatcher());
        realm.setCacheManager(cacheManager);
        realm.setAuthorizationCacheName("AuthorizationCache");
        realm.setAuthenticationCachingEnabled(false);
        realm.setAuthorizationCachingEnabled(true);
        realm.setRolePermissionResolver(rolePermissionResolver);
        return realm;
    }

    /**
     * 处理用户登录、注销、权限认证等核心业务
     */
    @Bean
    public DefaultWebSecurityManager securityManager(
            @Qualifier("realm") Realm realm,
            @Qualifier("sessionManager") SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager);
        securityManager.setRealm(realm);
        return securityManager;
    }

    /**
     * 登录过滤器
     */
    @Bean
    public CaptchaFormAuthenticationFilter authenticationFilter() {
        return new CaptchaFormAuthenticationFilter();
    }

    /**
     * 设置不向Web容器中注册登录过滤器
     */
    @Bean
    public FilterRegistrationBean unregistrationFormAuthenticationFilter(
            @Qualifier("authenticationFilter") FormAuthenticationFilter authenticationFilter) {
        FilterRegistrationBean<FormAuthenticationFilter> registration =
                new FilterRegistrationBean<>(authenticationFilter);
        registration.setEnabled(false);
        return registration;
    }

    /**
     * 注销过滤器
     */
    @Bean
    public LogoutFilter logoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/");
        return logoutFilter;
    }

    /**
     * 不向Web容器中注册注销过滤器
     */
    @Bean
    public FilterRegistrationBean<LogoutFilter> unregistrationLogoutFilter(
            @Qualifier("logoutFilter") LogoutFilter logoutFilter) {
        FilterRegistrationBean<LogoutFilter> registration =
                new FilterRegistrationBean<>(logoutFilter);
        registration.setEnabled(false);
        return registration;
    }

    /**
     * 核心过滤器
     */
    @Bean("shiroFilter")
    public DynamicShiroFilterFactoryBean shiroFilter(
            @Qualifier("securityManager") DefaultWebSecurityManager securityManager,
            @Qualifier("sysResourceMapper") SysResourceMapper sysResourceMapper,
            @Qualifier("logoutFilter") LogoutFilter logoutFilter,
            @Autowired SystemSettings systemSettings,
            @Qualifier("authenticationFilter") FormAuthenticationFilter authenticationFilter) {
        DynamicShiroFilterFactoryBean shiroFilterFactoryBean = new DynamicShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSysResourceMapper(sysResourceMapper);
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login");
        shiroFilterFactoryBean.setSuccessUrl(systemSettings.getLoginSuccessUrl());

        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("logout", logoutFilter);
        filterMap.put("authc", authenticationFilter);
        filterMap.put("auth", new RolePermissionAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/anonymous", "anon");
        filterChainDefinitionMap.put("/captcha", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/authc", "authc");
        filterChainDefinitionMap.put("/role", "roles[admin]");
        filterChainDefinitionMap.put("/permissive", "authc[permissive]");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
            @Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}
